import { isEmpty } from 'ramda'

export let db: {
	[x: string]: any
	onsuccess: () => void
	onerror: (event: any) => void
	transaction: (
		arg0: any[],
		arg1: string
	) => {
		(): any
		new (): any
		objectStore: {
			(arg0: any): {
				index(index: any): any
				(): any
				new (): any
				add: { (arg0: any): any; new (): any }
			}
			new (): any
		}
	}
}
let openRequest
const indexedDB =
	window.indexedDB || (window as any).webkitIndexedDb || (window as any).mozIndexed || (window as any).msIndexedDB

// transaction 事务处理 意味着一系列操作步骤之中，只要有一步失败，整个事务就都取消，数据库回滚到事务发生之前的状态，不存在只改写一部分数据的情况。

// tableList [{tableName,keyPath,indexName}]
/**
 * 初始化数据库
 * @param {string} dbName 数据库名
 * @param {Array} tableList 数据表列表
 */
// 初始化
export function init<T>(dbName: string, tableList?: T[] | any[]) {
	// 存在就打开 不存在新建 第二个参数为db 版本
	openRequest = indexedDB.open(dbName, 1)
	// 新的数据库创建 或者数据库的版本号被更改会被触发
	openRequest.onupgradeneeded = function (e) {
		// 表的创建在这个回调里执行
		const thisDb = (e.target as any)?.result
		console.log('running onupgradeneeded' + thisDb)
		if (tableList?.length) {
			tableList.forEach((table) => {
				if (!thisDb.objectStoreNames.contains(table.tableName)) {
					console.log('I need to create the objectStore')
					// keyPath 主键 autoIncrement 是否自增
					const objectStore = thisDb.createObjectStore(table.tableName, {
						keyPath: table.keyPath,
						...table.attr,
						autoIncrement: true,
					})
					if (table.indexName) {
						// 创建表的时候 可以去指定那些字段是可以被索引的字段
						objectStore.createIndex(table.indexName, table.indexName, {
							unique: table.unique || false,
						})
					}
				}
			})
		} else {
			console.error('请传入数据表参数')
		}
	}
	// 已经创建好的数据库创建成功的时候
	openRequest.onsuccess = function (e) {
		db = (e.target as any)?.result
		db.onerror = function (event) {
			console.error('Database error: ' + event.target.errorCode)
			console.dir(event.target)
		}
	}
	// 打开失败时调用
	openRequest.onerror = function (e) {
		console.error('openRequest.onerror', e)
	}
	// 关闭数据库重新新增表数据
	openRequest.onblocked = (event) => {
		console.log('blocked ...', event) // in my case this was triggering
		db?.close() // so close the connection and it worked
	}
}
interface AddState {
	id?: number
}
/**
 * 添加一行
 * @param {string} tableName 表名
 * @param {object} data 数据
 * @returns {promise}
 */
export function add<T>(tableName: string, data: T | AddState) {
	return new Promise<void>((suc, fail) => {
		;(async () => {
			// 判断是否存在，存在则更新，不存在则添加
			const getData = await readByMainKey({ tableName, key: (data as AddState).id as number })
			if (isEmpty(getData)) {
				const request = db.transaction([tableName], 'readwrite').objectStore(tableName).add(data)
				request.onsuccess = function (event: any) {
					suc()
					console.log('数据写入成功', event)
				}
				request.onerror = function (event: any) {
					fail()
					console.log('数据写入失败', event)
				}
			} else {
				update<T>(tableName, data as T)
			}
		})()
	})
}

/**
 * 遍历所有数据 其实这里有点坑 真正readAll是下面readAllPC这个方法，因为低版本webview手机不兼容objectStore.getAll所以使用游标来实现
 * @param {string} tableName 表名
 * @returns {promise}
 */
export function readAll(tableName: string) {
	return new Promise((suc, fail) => {
		const objectStore = db?.transaction([tableName], 'readwrite').objectStore(tableName)
		// 我这里做的是把所有的结果全部收集起来 当然我们可以做其他事情此处拿到的value是每条数据的结果、还有primaryKey主键、key、与direction
		const result: any[] = []
		;(objectStore as any).openCursor().onsuccess = function (event: { target: { result: any } }) {
			const cursor = event.target.result
			if (cursor) {
				result?.push({ ...cursor.value })
				cursor.continue()
			} else {
				suc(result)
			}
		}
		;(objectStore as any).onerror = function (event: any) {
			console.dir(event)
			fail()
		}
	})
}

/**
 * 读取所有数据 仅在pc上或者版本高的手机浏览器中使用 readAllPC这个方法 这个在低版本weview的手机浏览器里面不兼容
 * @param {string} tableName
 * @returns {promise}
 */
export function readAllForHighVersion(tableName: string) {
	return new Promise((suc, fail) => {
		const objectStore = db.transaction([tableName], 'readwrite').objectStore(tableName)
		const request = (objectStore as any).getAll()
		request.onerror = function (event: any) {
			fail()
			console.log('readAll--->读取表事务失败', event)
		}
		request.onsuccess = function () {
			suc(request.result || [])
		}
	})
}
interface ReadByMainKey {
	tableName: string
	key: string | number
}
/**
 * 根据主键查询对应数据
 * @param {string} tableName 表名
 * @param {string} key 主键
 * @returns {promise}
 */
export function readByMainKey({ tableName, key }: ReadByMainKey) {
	return new Promise((suc, fail) => {
		if (!key) {
			fail()
			return
		}
		const objectStore = db.transaction([tableName], 'readwrite').objectStore(tableName)
		const request = (objectStore as any).get(key)

		request.onerror = function (event: any) {
			console.error('根据主键查询对应数据', event)
			fail()
		}

		request.onsuccess = function () {
			suc(request.result || {})
		}
	})
}

/**
 * 根据主键删除对应数据
 * @param {string} tableName 表名
 * @param {string} key 主键
 * @returns {promise}
 */
export function remove({ tableName, key }: ReadByMainKey) {
	return new Promise<void>((suc, fail) => {
		const objectStore = db.transaction([tableName], 'readwrite').objectStore(tableName)
		const request = (objectStore as any).delete(key)

		request.onerror = function (event: any) {
			console.error('更新失败', event)
			fail()
		}

		request.onsuccess = function (event: any) {
			console.log('删除成功', event)
			suc()
		}
	})
}

/**
 * 根据主键更新对应数据
 * @param {string} tableName 表名
 * @param {object} data 对应的主键与值 和 数据
 * @returns {promise}
 */
export function update<T>(tableName: string, data: T) {
	return new Promise<void>((suc, fail) => {
		const objectStore = db.transaction([tableName], 'readwrite').objectStore(tableName)
		const request = (objectStore as any).put(data)

		request.onerror = function (event: any) {
			console.error('更新失败', event)
			fail()
		}

		request.onsuccess = function (event: any) {
			console.log('更新成功', event)
			suc()
		}
	})
}
interface ReadByIndex {
	tableName: string
	indexName: any
	indexVal: number
}
/**
 * 通过索引查找对应数据
 * @param {string} indexName 索引名称
 * @param {number} indexVal index 索引值
 * @param {string} tableName 表名
 * @returns {promise}
 */
export function readByIndex({ tableName, indexName, indexVal }: ReadByIndex) {
	return new Promise((suc, fail) => {
		const objectStore = db.transaction([tableName], 'readwrite').objectStore(tableName)
		// 假定新建表格的时候，对name字段建立了索引。
		// objectStore.createIndex('name', 'name', { unique: false });
		const index = (objectStore as any).index(indexName)
		const request = index.get(indexVal)

		request.onerror = function (event: any) {
			console.log('事务失败', event)
			fail()
		}

		request.onsuccess = function (event: any) {
			if (request.result) {
				suc(request.result)
			} else {
				console.log('未获得数据记录', event)
			}
		}
	})
}

/**
 * 删除数据库
 * @param {string} DB_NAME 数据库名称
 * @returns
 */
export async function deleteDB(DB_NAME: string) {
	return indexedDB.deleteDatabase(DB_NAME)
}
/**
 * 关闭数据库
 * @param {string} DB_NAME 数据库名称
 * @returns
 */
export function closeDB(DB_NAME: any) {
	return (indexedDB as any).close(DB_NAME)
}
/**
 * 清除表
 * @param {string} tableName
 * @returns {promise}
 */
export function clearTable(tableName: any) {
	return new Promise<void>((suc, fail) => {
		const objectStore = db.transaction([tableName], 'readwrite').objectStore(tableName)
		const request = (objectStore as any).clear()

		request.onerror = function (event: any) {
			console.log('事务失败', event)
			fail()
		}

		request.onsuccess = function (event: any) {
			console.log('清除成功', event)
			suc()
		}
	})
}
interface CursorGetDataByIndex {
	storeName: string
	indexName: string
	indexValue: number
}
/**
 * 通过索引和游标查询记录
 * @param {object} db 数据库实例
 * @param {string} storeName 仓库名称
 * @param {string} indexName 索引名称
 * @param {string} indexValue 索引值
 */
export function cursorGetDataByIndex({ storeName, indexName, indexValue }: CursorGetDataByIndex) {
	const list: any[] = []
	const store = db.transaction([storeName], 'readwrite').objectStore(storeName) // 仓库对象
	const request = store
		.index(indexName) // 索引对象
		.openCursor(IDBKeyRange.only(indexValue)) // 指针对象
	request.onsuccess = function (e: { target: { result: any } }) {
		const cursor = e.target.result
		if (cursor) {
			// 必须要检查
			list.push(cursor.value)
			cursor.continue() // 遍历了存储对象中的所有内容
		} else {
			console.log('游标索引查询结果：', list)
		}
	}
}
